﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Printing;
using System.ComponentModel;
using WebKit.Interop;
using System.Drawing;
using System.Windows.Forms;

namespace WebKit
{
    internal class PrintManager
    {
        private PrintDocument _document;
        private IWebFramePrivate _webFramePrivate;
        private IWebKitBrowserHost _owner;
        private IWebKitBrowser _browser;
        private Graphics _printGfx;
        private uint _nPages;
        private uint _page;
        private int _hDC;
        private bool _preview;
        private bool _printing = false;

        public PrintManager(PrintDocument Document, IWebKitBrowserHost Owner, IWebKitBrowser Browser, bool Preview)
        {
            this._document = Document;
            this._owner = Owner;
            this._browser = Browser;
            this._webFramePrivate =
                (IWebFramePrivate)((IWebView)_browser.GetWebView()).mainFrame();
            this._preview = Preview;
        }

        public void Print()
        {
            // potential concurrency issues with shared state variable
            if (_printing)
                return;
            _printing = true;

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerAsync();
        }

        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            _document.PrintPage += new PrintPageEventHandler(_document_PrintPage);
            if (!_preview)
                _document.Print();

            _printing = false;
        }

        private delegate uint GetPrintedPageCountDelegate();

        private void _document_PrintPage(object sender, PrintPageEventArgs e)
        {
            // running on a seperate thread, so we invoke _webFramePrivate
            // methods on the owners ui thread

            if (_printGfx == null)
            {
                // initialise printing
                _printGfx = e.Graphics;
                _hDC = _printGfx.GetHdc().ToInt32();

                _ownerInvoke(() => _webFramePrivate.setInPrintingMode(1, _hDC));

                _nPages = _ownerInvoke(() =>
                {
                    return _webFramePrivate.getPrintedPageCount(_hDC);
                });

                _page = 1;
            }

            _ownerInvoke(() => _webFramePrivate.spoolPages(_hDC, _page, _page, IntPtr.Zero));

            ++_page;
            if (_page <= _nPages)
            {
                e.HasMorePages = true;
            }
            else
            {
                _ownerInvoke(() => _webFramePrivate.setInPrintingMode(0, _hDC));
                e.HasMorePages = false;
                _printGfx = null;
                _nPages = 0;
            }
        }

        private delegate TResult Fn<TResult>();
        private delegate void Fn();

        private TResult _ownerInvoke<TResult>(Fn<TResult> method)
        {
            if (_owner.InvokeRequired)
                return (TResult)_owner.Invoke(method);
            else
                return method();
        }

        private void _ownerInvoke(Fn method)
        {
            if (_owner.InvokeRequired)
                _owner.Invoke(method);
            else
                method();
        }
    }
}
